1   /*
2    * Copyright (c) 1999, 2006, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package java.awt;
27  
28  /**
29   * A set of attributes which control a print job.
30   * <p>
31   * Instances of this class control the number of copies, default selection,
32   * destination, print dialog, file and printer names, page ranges, multiple
33   * document handling (including collation), and multi-page imposition (such
34   * as duplex) of every print job which uses the instance. Attribute names are
35   * compliant with the Internet Printing Protocol (IPP) 1.1 where possible.
36   * Attribute values are partially compliant where possible.
37   * <p>
38   * To use a method which takes an inner class type, pass a reference to
39   * one of the constant fields of the inner class. Client code cannot create
40   * new instances of the inner class types because none of those classes
41   * has a public constructor. For example, to set the print dialog type to
42   * the cross-platform, pure Java print dialog, use the following code:
43   * <pre>
44   * import java.awt.JobAttributes;
45   *
46   * public class PureJavaPrintDialogExample {
47   *     public void setPureJavaPrintDialog(JobAttributes jobAttributes) {
48   *         jobAttributes.setDialog(JobAttributes.DialogType.COMMON);
49   *     }
50   * }
51   * </pre>
52   * <p>
53   * Every IPP attribute which supports an <i>attributeName</i>-default value
54   * has a corresponding <code>set<i>attributeName</i>ToDefault</code> method.
55   * Default value fields are not provided.
56   *
57   * @author      David Mendenhall
58   * @since 1.3
59   */
60  public final class JobAttributes implements Cloneable {
61      /**
62       * A type-safe enumeration of possible default selection states.
63       * @since 1.3
64       */
65      public static final class DefaultSelectionType extends AttributeValue {
66          private static final int I_ALL = 0;
67          private static final int I_RANGE = 1;
68          private static final int I_SELECTION = 2;
69  
70          private static final String NAMES[] = {
71              "all", "range", "selection"
72          };
73  
74          /**
75           * The <code>DefaultSelectionType</code> instance to use for
76           * specifying that all pages of the job should be printed.
77           */
78          public static final DefaultSelectionType ALL =
79             new DefaultSelectionType(I_ALL);
80          /**
81           * The <code>DefaultSelectionType</code> instance to use for
82           * specifying that a range of pages of the job should be printed.
83           */
84          public static final DefaultSelectionType RANGE =
85             new DefaultSelectionType(I_RANGE);
86          /**
87           * The <code>DefaultSelectionType</code> instance to use for
88           * specifying that the current selection should be printed.
89           */
90          public static final DefaultSelectionType SELECTION =
91             new DefaultSelectionType(I_SELECTION);
92  
93          private DefaultSelectionType(int type) {
94              super(type, NAMES);
95          }
96      }
97  
98      /**
99       * A type-safe enumeration of possible job destinations.
100      * @since 1.3
101      */
102     public static final class DestinationType extends AttributeValue {
103         private static final int I_FILE = 0;
104         private static final int I_PRINTER = 1;
105 
106         private static final String NAMES[] = {
107             "file", "printer"
108         };
109 
110         /**
111          * The <code>DestinationType</code> instance to use for
112          * specifying print to file.
113          */
114         public static final DestinationType FILE =
115             new DestinationType(I_FILE);
116         /**
117          * The <code>DestinationType</code> instance to use for
118          * specifying print to printer.
119          */
120         public static final DestinationType PRINTER =
121             new DestinationType(I_PRINTER);
122 
123         private DestinationType(int type) {
124             super(type, NAMES);
125         }
126     }
127 
128     /**
129      * A type-safe enumeration of possible dialogs to display to the user.
130      * @since 1.3
131      */
132     public static final class DialogType extends AttributeValue {
133         private static final int I_COMMON = 0;
134         private static final int I_NATIVE = 1;
135         private static final int I_NONE = 2;
136 
137         private static final String NAMES[] = {
138             "common", "native", "none"
139         };
140 
141         /**
142          * The <code>DialogType</code> instance to use for
143          * specifying the cross-platform, pure Java print dialog.
144          */
145         public static final DialogType COMMON = new DialogType(I_COMMON);
146         /**
147          * The <code>DialogType</code> instance to use for
148          * specifying the platform's native print dialog.
149          */
150         public static final DialogType NATIVE = new DialogType(I_NATIVE);
151         /**
152          * The <code>DialogType</code> instance to use for
153          * specifying no print dialog.
154          */
155         public static final DialogType NONE = new DialogType(I_NONE);
156 
157         private DialogType(int type) {
158             super(type, NAMES);
159         }
160     }
161 
162     /**
163      * A type-safe enumeration of possible multiple copy handling states.
164      * It is used to control how the sheets of multiple copies of a single
165      * document are collated.
166      * @since 1.3
167      */
168     public static final class MultipleDocumentHandlingType extends
169                                                                AttributeValue {
170         private static final int I_SEPARATE_DOCUMENTS_COLLATED_COPIES = 0;
171         private static final int I_SEPARATE_DOCUMENTS_UNCOLLATED_COPIES = 1;
172 
173         private static final String NAMES[] = {
174             "separate-documents-collated-copies",
175             "separate-documents-uncollated-copies"
176         };
177 
178         /**
179          * The <code>MultipleDocumentHandlingType</code> instance to use for specifying
180          * that the job should be divided into separate, collated copies.
181          */
182         public static final MultipleDocumentHandlingType
183             SEPARATE_DOCUMENTS_COLLATED_COPIES =
184                 new MultipleDocumentHandlingType(
185                     I_SEPARATE_DOCUMENTS_COLLATED_COPIES);
186         /**
187          * The <code>MultipleDocumentHandlingType</code> instance to use for specifying
188          * that the job should be divided into separate, uncollated copies.
189          */
190         public static final MultipleDocumentHandlingType
191             SEPARATE_DOCUMENTS_UNCOLLATED_COPIES =
192                 new MultipleDocumentHandlingType(
193                     I_SEPARATE_DOCUMENTS_UNCOLLATED_COPIES);
194 
195         private MultipleDocumentHandlingType(int type) {
196             super(type, NAMES);
197         }
198     }
199 
200     /**
201      * A type-safe enumeration of possible multi-page impositions. These
202      * impositions are in compliance with IPP 1.1.
203      * @since 1.3
204      */
205     public static final class SidesType extends AttributeValue {
206         private static final int I_ONE_SIDED = 0;
207         private static final int I_TWO_SIDED_LONG_EDGE = 1;
208         private static final int I_TWO_SIDED_SHORT_EDGE = 2;
209 
210         private static final String NAMES[] = {
211             "one-sided", "two-sided-long-edge", "two-sided-short-edge"
212         };
213 
214         /**
215          * The <code>SidesType</code> instance to use for specifying that
216          * consecutive job pages should be printed upon the same side of
217          * consecutive media sheets.
218          */
219         public static final SidesType ONE_SIDED = new SidesType(I_ONE_SIDED);
220         /**
221          * The <code>SidesType</code> instance to use for specifying that
222          * consecutive job pages should be printed upon front and back sides
223          * of consecutive media sheets, such that the orientation of each pair
224          * of pages on the medium would be correct for the reader as if for
225          * binding on the long edge.
226          */
227         public static final SidesType TWO_SIDED_LONG_EDGE =
228             new SidesType(I_TWO_SIDED_LONG_EDGE);
229         /**
230          * The <code>SidesType</code> instance to use for specifying that
231          * consecutive job pages should be printed upon front and back sides
232          * of consecutive media sheets, such that the orientation of each pair
233          * of pages on the medium would be correct for the reader as if for
234          * binding on the short edge.
235          */
236         public static final SidesType TWO_SIDED_SHORT_EDGE =
237             new SidesType(I_TWO_SIDED_SHORT_EDGE);
238 
239         private SidesType(int type) {
240             super(type, NAMES);
241         }
242     }
243 
244     private int copies;
245     private DefaultSelectionType defaultSelection;
246     private DestinationType destination;
247     private DialogType dialog;
248     private String fileName;
249     private int fromPage;
250     private int maxPage;
251     private int minPage;
252     private MultipleDocumentHandlingType multipleDocumentHandling;
253     private int[][] pageRanges;
254     private int prFirst;
255     private int prLast;
256     private String printer;
257     private SidesType sides;
258     private int toPage;
259 
260     /**
261      * Constructs a <code>JobAttributes</code> instance with default
262      * values for every attribute.  The dialog defaults to
263      * <code>DialogType.NATIVE</code>.  Min page defaults to
264      * <code>1</code>.  Max page defaults to <code>Integer.MAX_VALUE</code>.
265      * Destination defaults to <code>DestinationType.PRINTER</code>.
266      * Selection defaults to <code>DefaultSelectionType.ALL</code>.
267      * Number of copies defaults to <code>1</code>. Multiple document handling defaults
268      * to <code>MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_UNCOLLATED_COPIES</code>.
269      * Sides defaults to <code>SidesType.ONE_SIDED</code>. File name defaults
270      * to <code>null</code>.
271      */
272     public JobAttributes() {
273         setCopiesToDefault();
274         setDefaultSelection(DefaultSelectionType.ALL);
275         setDestination(DestinationType.PRINTER);
276         setDialog(DialogType.NATIVE);
277         setMaxPage(Integer.MAX_VALUE);
278         setMinPage(1);
279         setMultipleDocumentHandlingToDefault();
280         setSidesToDefault();
281     }
282 
283     /**
284      * Constructs a <code>JobAttributes</code> instance which is a copy
285      * of the supplied <code>JobAttributes</code>.
286      *
287      * @param   obj the <code>JobAttributes</code> to copy
288      */
289     public JobAttributes(JobAttributes obj) {
290         set(obj);
291     }
292 
293     /**
294      * Constructs a <code>JobAttributes</code> instance with the
295      * specified values for every attribute.
296      *
297      * @param   copies an integer greater than 0
298      * @param   defaultSelection <code>DefaultSelectionType.ALL</code>,
299      *          <code>DefaultSelectionType.RANGE</code>, or
300      *          <code>DefaultSelectionType.SELECTION</code>
301      * @param   destination <code>DesintationType.FILE</code> or
302      *          <code>DesintationType.PRINTER</code>
303      * @param   dialog <code>DialogType.COMMON</code>,
304      *          <code>DialogType.NATIVE</code>, or
305      *          <code>DialogType.NONE</code>
306      * @param   fileName the possibly <code>null</code> file name
307      * @param   maxPage an integer greater than zero and greater than or equal
308      *          to <i>minPage</i>
309      * @param   minPage an integer greater than zero and less than or equal
310      *          to <i>maxPage</i>
311      * @param   multipleDocumentHandling
312      *     <code>MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_COLLATED_COPIES</code> or
313      *     <code>MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_UNCOLLATED_COPIES</code>
314      * @param   pageRanges an array of integer arrays of two elements; an array
315      *          is interpreted as a range spanning all pages including and
316      *          between the specified pages; ranges must be in ascending
317      *          order and must not overlap; specified page numbers cannot be
318      *          less than <i>minPage</i> nor greater than <i>maxPage</i>;
319      *          for example:
320      *          <pre>
321      *          (new int[][] { new int[] { 1, 3 }, new int[] { 5, 5 },
322      *                         new int[] { 15, 19 } }),
323      *          </pre>
324      *          specifies pages 1, 2, 3, 5, 15, 16, 17, 18, and 19. Note that
325      *          (<code>new int[][] { new int[] { 1, 1 }, new int[] { 1, 2 } }</code>),
326      *          is an invalid set of page ranges because the two ranges
327      *          overlap
328      * @param   printer the possibly <code>null</code> printer name
329      * @param   sides <code>SidesType.ONE_SIDED</code>,
330      *          <code>SidesType.TWO_SIDED_LONG_EDGE</code>, or
331      *          <code>SidesType.TWO_SIDED_SHORT_EDGE</code>
332      * @throws  IllegalArgumentException if one or more of the above
333      *          conditions is violated
334      */
335     public JobAttributes(int copies, DefaultSelectionType defaultSelection,
336                          DestinationType destination, DialogType dialog,
337                          String fileName, int maxPage, int minPage,
338                          MultipleDocumentHandlingType multipleDocumentHandling,
339                          int[][] pageRanges, String printer, SidesType sides) {
340         setCopies(copies);
341         setDefaultSelection(defaultSelection);
342         setDestination(destination);
343         setDialog(dialog);
344         setFileName(fileName);
345         setMaxPage(maxPage);
346         setMinPage(minPage);
347         setMultipleDocumentHandling(multipleDocumentHandling);
348         setPageRanges(pageRanges);
349         setPrinter(printer);
350         setSides(sides);
351     }
352 
353     /**
354      * Creates and returns a copy of this <code>JobAttributes</code>.
355      *
356      * @return  the newly created copy; it is safe to cast this Object into
357      *          a <code>JobAttributes</code>
358      */
359     public Object clone() {
360         try {
361             return super.clone();
362         } catch (CloneNotSupportedException e) {
363             // Since we implement Cloneable, this should never happen
364             throw new InternalError();
365         }
366     }
367 
368     /**
369      * Sets all of the attributes of this <code>JobAttributes</code> to
370      * the same values as the attributes of obj.
371      *
372      * @param   obj the <code>JobAttributes</code> to copy
373      */
374     public void set(JobAttributes obj) {
375         copies = obj.copies;
376         defaultSelection = obj.defaultSelection;
377         destination = obj.destination;
378         dialog = obj.dialog;
379         fileName = obj.fileName;
380         fromPage = obj.fromPage;
381         maxPage = obj.maxPage;
382         minPage = obj.minPage;
383         multipleDocumentHandling = obj.multipleDocumentHandling;
384         // okay because we never modify the contents of pageRanges
385         pageRanges = obj.pageRanges;
386         prFirst = obj.prFirst;
387         prLast = obj.prLast;
388         printer = obj.printer;
389         sides = obj.sides;
390         toPage = obj.toPage;
391     }
392 
393     /**
394      * Returns the number of copies the application should render for jobs
395      * using these attributes. This attribute is updated to the value chosen
396      * by the user.
397      *
398      * @return  an integer greater than 0.
399      */
400     public int getCopies() {
401         return copies;
402     }
403 
404     /**
405      * Specifies the number of copies the application should render for jobs
406      * using these attributes. Not specifying this attribute is equivalent to
407      * specifying <code>1</code>.
408      *
409      * @param   copies an integer greater than 0
410      * @throws  IllegalArgumentException if <code>copies</code> is less than
411      *      or equal to 0
412      */
413     public void setCopies(int copies) {
414         if (copies <= 0) {
415             throw new IllegalArgumentException("Invalid value for attribute "+
416                                                "copies");
417         }
418         this.copies = copies;
419     }
420 
421     /**
422      * Sets the number of copies the application should render for jobs using
423      * these attributes to the default. The default number of copies is 1.
424      */
425     public void setCopiesToDefault() {
426         setCopies(1);
427     }
428 
429     /**
430      * Specifies whether, for jobs using these attributes, the application
431      * should print all pages, the range specified by the return value of
432      * <code>getPageRanges</code>, or the current selection. This attribute
433      * is updated to the value chosen by the user.
434      *
435      * @return  DefaultSelectionType.ALL, DefaultSelectionType.RANGE, or
436      *          DefaultSelectionType.SELECTION
437      */
438     public DefaultSelectionType getDefaultSelection() {
439         return defaultSelection;
440     }
441 
442     /**
443      * Specifies whether, for jobs using these attributes, the application
444      * should print all pages, the range specified by the return value of
445      * <code>getPageRanges</code>, or the current selection. Not specifying
446      * this attribute is equivalent to specifying DefaultSelectionType.ALL.
447      *
448      * @param   defaultSelection DefaultSelectionType.ALL,
449      *          DefaultSelectionType.RANGE, or DefaultSelectionType.SELECTION.
450      * @throws  IllegalArgumentException if defaultSelection is <code>null</code>
451      */
452     public void setDefaultSelection(DefaultSelectionType defaultSelection) {
453         if (defaultSelection == null) {
454             throw new IllegalArgumentException("Invalid value for attribute "+
455                                                "defaultSelection");
456         }
457         this.defaultSelection = defaultSelection;
458     }
459 
460     /**
461      * Specifies whether output will be to a printer or a file for jobs using
462      * these attributes. This attribute is updated to the value chosen by the
463      * user.
464      *
465      * @return  DesintationType.FILE or DesintationType.PRINTER
466      */
467     public DestinationType getDestination() {
468         return destination;
469     }
470 
471     /**
472      * Specifies whether output will be to a printer or a file for jobs using
473      * these attributes. Not specifying this attribute is equivalent to
474      * specifying DesintationType.PRINTER.
475      *
476      * @param   destination DesintationType.FILE or DesintationType.PRINTER.
477      * @throws  IllegalArgumentException if destination is null.
478      */
479     public void setDestination(DestinationType destination) {
480         if (destination == null) {
481             throw new IllegalArgumentException("Invalid value for attribute "+
482                                                "destination");
483         }
484         this.destination = destination;
485     }
486 
487     /**
488      * Returns whether, for jobs using these attributes, the user should see
489      * a print dialog in which to modify the print settings, and which type of
490      * print dialog should be displayed. DialogType.COMMON denotes a cross-
491      * platform, pure Java print dialog. DialogType.NATIVE denotes the
492      * platform's native print dialog. If a platform does not support a native
493      * print dialog, the pure Java print dialog is displayed instead.
494      * DialogType.NONE specifies no print dialog (i.e., background printing).
495      * This attribute cannot be modified by, and is not subject to any
496      * limitations of, the implementation or the target printer.
497      *
498      * @return  <code>DialogType.COMMON</code>, <code>DialogType.NATIVE</code>, or
499      *          <code>DialogType.NONE</code>
500      */
501     public DialogType getDialog() {
502         return dialog;
503     }
504 
505     /**
506      * Specifies whether, for jobs using these attributes, the user should see
507      * a print dialog in which to modify the print settings, and which type of
508      * print dialog should be displayed. DialogType.COMMON denotes a cross-
509      * platform, pure Java print dialog. DialogType.NATIVE denotes the
510      * platform's native print dialog. If a platform does not support a native
511      * print dialog, the pure Java print dialog is displayed instead.
512      * DialogType.NONE specifies no print dialog (i.e., background printing).
513      * Not specifying this attribute is equivalent to specifying
514      * DialogType.NATIVE.
515      *
516      * @param   dialog DialogType.COMMON, DialogType.NATIVE, or
517      *          DialogType.NONE.
518      * @throws  IllegalArgumentException if dialog is null.
519      */
520     public void setDialog(DialogType dialog) {
521         if (dialog == null) {
522             throw new IllegalArgumentException("Invalid value for attribute "+
523                                                "dialog");
524         }
525         this.dialog = dialog;
526     }
527 
528     /**
529      * Specifies the file name for the output file for jobs using these
530      * attributes. This attribute is updated to the value chosen by the user.
531      *
532      * @return  the possibly <code>null</code> file name
533      */
534     public String getFileName() {
535         return fileName;
536     }
537 
538     /**
539      * Specifies the file name for the output file for jobs using these
540      * attributes. Default is platform-dependent and implementation-defined.
541      *
542      * @param   fileName the possibly null file name.
543      */
544     public void setFileName(String fileName) {
545         this.fileName = fileName;
546     }
547 
548     /**
549      * Returns, for jobs using these attributes, the first page to be
550      * printed, if a range of pages is to be printed. This attribute is
551      * updated to the value chosen by the user. An application should ignore
552      * this attribute on output, unless the return value of the <code>
553      * getDefaultSelection</code> method is DefaultSelectionType.RANGE. An
554      * application should honor the return value of <code>getPageRanges</code>
555      * over the return value of this method, if possible.
556      *
557      * @return  an integer greater than zero and less than or equal to
558      *          <i>toPage</i> and greater than or equal to <i>minPage</i> and
559      *          less than or equal to <i>maxPage</i>.
560      */
561     public int getFromPage() {
562         if (fromPage != 0) {
563             return fromPage;
564         } else if (toPage != 0) {
565             return getMinPage();
566         } else if (pageRanges != null) {
567             return prFirst;
568         } else {
569             return getMinPage();
570         }
571     }
572 
573     /**
574      * Specifies, for jobs using these attributes, the first page to be
575      * printed, if a range of pages is to be printed. If this attribute is not
576      * specified, then the values from the pageRanges attribute are used. If
577      * pageRanges and either or both of fromPage and toPage are specified,
578      * pageRanges takes precedence. Specifying none of pageRanges, fromPage,
579      * or toPage is equivalent to calling
580      * setPageRanges(new int[][] { new int[] { <i>minPage</i> } });
581      *
582      * @param   fromPage an integer greater than zero and less than or equal to
583      *          <i>toPage</i> and greater than or equal to <i>minPage</i> and
584      *          less than or equal to <i>maxPage</i>.
585      * @throws  IllegalArgumentException if one or more of the above
586      *          conditions is violated.
587      */
588     public void setFromPage(int fromPage) {
589         if (fromPage <= 0 ||
590             (toPage != 0 && fromPage > toPage) ||
591             fromPage < minPage ||
592             fromPage > maxPage) {
593             throw new IllegalArgumentException("Invalid value for attribute "+
594                                                "fromPage");
595         }
596         this.fromPage = fromPage;
597     }
598 
599     /**
600      * Specifies the maximum value the user can specify as the last page to
601      * be printed for jobs using these attributes. This attribute cannot be
602      * modified by, and is not subject to any limitations of, the
603      * implementation or the target printer.
604      *
605      * @return  an integer greater than zero and greater than or equal
606      *          to <i>minPage</i>.
607      */
608     public int getMaxPage() {
609         return maxPage;
610     }
611 
612     /**
613      * Specifies the maximum value the user can specify as the last page to
614      * be printed for jobs using these attributes. Not specifying this
615      * attribute is equivalent to specifying <code>Integer.MAX_VALUE</code>.
616      *
617      * @param   maxPage an integer greater than zero and greater than or equal
618      *          to <i>minPage</i>
619      * @throws  IllegalArgumentException if one or more of the above
620      *          conditions is violated
621      */
622     public void setMaxPage(int maxPage) {
623         if (maxPage <= 0 || maxPage < minPage) {
624             throw new IllegalArgumentException("Invalid value for attribute "+
625                                                "maxPage");
626         }
627         this.maxPage = maxPage;
628     }
629 
630     /**
631      * Specifies the minimum value the user can specify as the first page to
632      * be printed for jobs using these attributes. This attribute cannot be
633      * modified by, and is not subject to any limitations of, the
634      * implementation or the target printer.
635      *
636      * @return  an integer greater than zero and less than or equal
637      *          to <i>maxPage</i>.
638      */
639     public int getMinPage() {
640         return minPage;
641     }
642 
643     /**
644      * Specifies the minimum value the user can specify as the first page to
645      * be printed for jobs using these attributes. Not specifying this
646      * attribute is equivalent to specifying <code>1</code>.
647      *
648      * @param   minPage an integer greater than zero and less than or equal
649      *          to <i>maxPage</i>.
650      * @throws  IllegalArgumentException if one or more of the above
651      *          conditions is violated.
652      */
653     public void setMinPage(int minPage) {
654         if (minPage <= 0 || minPage > maxPage) {
655             throw new IllegalArgumentException("Invalid value for attribute "+
656                                                "minPage");
657         }
658         this.minPage = minPage;
659     }
660 
661     /**
662      * Specifies the handling of multiple copies, including collation, for
663      * jobs using these attributes. This attribute is updated to the value
664      * chosen by the user.
665      *
666      * @return
667      *     MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_COLLATED_COPIES or
668      *     MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_UNCOLLATED_COPIES.
669      */
670     public MultipleDocumentHandlingType getMultipleDocumentHandling() {
671         return multipleDocumentHandling;
672     }
673 
674     /**
675      * Specifies the handling of multiple copies, including collation, for
676      * jobs using these attributes. Not specifying this attribute is equivalent
677      * to specifying
678      * MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_UNCOLLATED_COPIES.
679      *
680      * @param   multipleDocumentHandling
681      *     MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_COLLATED_COPIES or
682      *     MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_UNCOLLATED_COPIES.
683      * @throws  IllegalArgumentException if multipleDocumentHandling is null.
684      */
685     public void setMultipleDocumentHandling(MultipleDocumentHandlingType
686                                             multipleDocumentHandling) {
687         if (multipleDocumentHandling == null) {
688             throw new IllegalArgumentException("Invalid value for attribute "+
689                                                "multipleDocumentHandling");
690         }
691         this.multipleDocumentHandling = multipleDocumentHandling;
692     }
693 
694     /**
695      * Sets the handling of multiple copies, including collation, for jobs
696      * using these attributes to the default. The default handling is
697      * MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_UNCOLLATED_COPIES.
698      */
699     public void setMultipleDocumentHandlingToDefault() {
700         setMultipleDocumentHandling(
701             MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_UNCOLLATED_COPIES);
702     }
703 
704     /**
705      * Specifies, for jobs using these attributes, the ranges of pages to be
706      * printed, if a range of pages is to be printed. All range numbers are
707      * inclusive. This attribute is updated to the value chosen by the user.
708      * An application should ignore this attribute on output, unless the
709      * return value of the <code>getDefaultSelection</code> method is
710      * DefaultSelectionType.RANGE.
711      *
712      * @return  an array of integer arrays of 2 elements. An array
713      *          is interpreted as a range spanning all pages including and
714      *          between the specified pages. Ranges must be in ascending
715      *          order and must not overlap. Specified page numbers cannot be
716      *          less than <i>minPage</i> nor greater than <i>maxPage</i>.
717      *          For example:
718      *          (new int[][] { new int[] { 1, 3 }, new int[] { 5, 5 },
719      *                         new int[] { 15, 19 } }),
720      *          specifies pages 1, 2, 3, 5, 15, 16, 17, 18, and 19.
721      */
722     public int[][] getPageRanges() {
723         if (pageRanges != null) {
724             // Return a copy because otherwise client code could circumvent the
725             // the checks made in setPageRanges by modifying the returned
726             // array.
727             int[][] copy = new int[pageRanges.length][2];
728             for (int i = 0; i < pageRanges.length; i++) {
729                 copy[i][0] = pageRanges[i][0];
730                 copy[i][1] = pageRanges[i][1];
731             }
732             return copy;
733         } else if (fromPage != 0 || toPage != 0) {
734             int fromPage = getFromPage();
735             int toPage = getToPage();
736             return new int[][] { new int[] { fromPage, toPage } };
737         } else {
738             int minPage = getMinPage();
739             return new int[][] { new int[] { minPage, minPage } };
740         }
741     }
742 
743     /**
744      * Specifies, for jobs using these attributes, the ranges of pages to be
745      * printed, if a range of pages is to be printed. All range numbers are
746      * inclusive. If this attribute is not specified, then the values from the
747      * fromPage and toPages attributes are used. If pageRanges and either or
748      * both of fromPage and toPage are specified, pageRanges takes precedence.
749      * Specifying none of pageRanges, fromPage, or toPage is equivalent to
750      * calling setPageRanges(new int[][] { new int[] { <i>minPage</i>,
751      *                                                 <i>minPage</i> } });
752      *
753      * @param   pageRanges an array of integer arrays of 2 elements. An array
754      *          is interpreted as a range spanning all pages including and
755      *          between the specified pages. Ranges must be in ascending
756      *          order and must not overlap. Specified page numbers cannot be
757      *          less than <i>minPage</i> nor greater than <i>maxPage</i>.
758      *          For example:
759      *          (new int[][] { new int[] { 1, 3 }, new int[] { 5, 5 },
760      *                         new int[] { 15, 19 } }),
761      *          specifies pages 1, 2, 3, 5, 15, 16, 17, 18, and 19. Note that
762      *          (new int[][] { new int[] { 1, 1 }, new int[] { 1, 2 } }),
763      *          is an invalid set of page ranges because the two ranges
764      *          overlap.
765      * @throws  IllegalArgumentException if one or more of the above
766      *          conditions is violated.
767      */
768     public void setPageRanges(int[][] pageRanges) {
769         String xcp = "Invalid value for attribute pageRanges";
770         int first = 0;
771         int last = 0;
772 
773         if (pageRanges == null) {
774             throw new IllegalArgumentException(xcp);
775         }
776 
777         for (int i = 0; i < pageRanges.length; i++) {
778             if (pageRanges[i] == null ||
779                 pageRanges[i].length != 2 ||
780                 pageRanges[i][0] <= last ||
781                 pageRanges[i][1] < pageRanges[i][0]) {
782                     throw new IllegalArgumentException(xcp);
783             }
784             last = pageRanges[i][1];
785             if (first == 0) {
786                 first = pageRanges[i][0];
787             }
788         }
789 
790         if (first < minPage || last > maxPage) {
791             throw new IllegalArgumentException(xcp);
792         }
793 
794         // Store a copy because otherwise client code could circumvent the
795         // the checks made above by holding a reference to the array and
796         // modifying it after calling setPageRanges.
797         int[][] copy = new int[pageRanges.length][2];
798         for (int i = 0; i < pageRanges.length; i++) {
799             copy[i][0] = pageRanges[i][0];
800             copy[i][1] = pageRanges[i][1];
801         }
802         this.pageRanges = copy;
803         this.prFirst = first;
804         this.prLast = last;
805     }
806 
807     /**
808      * Returns the destination printer for jobs using these attributes. This
809      * attribute is updated to the value chosen by the user.
810      *
811      * @return  the possibly null printer name.
812      */
813     public String getPrinter() {
814         return printer;
815     }
816 
817     /**
818      * Specifies the destination printer for jobs using these attributes.
819      * Default is platform-dependent and implementation-defined.
820      *
821      * @param   printer the possibly null printer name.
822      */
823     public void setPrinter(String printer) {
824         this.printer = printer;
825     }
826 
827     /**
828      * Returns how consecutive pages should be imposed upon the sides of the
829      * print medium for jobs using these attributes. SidesType.ONE_SIDED
830      * imposes each consecutive page upon the same side of consecutive media
831      * sheets. This imposition is sometimes called <i>simplex</i>.
832      * SidesType.TWO_SIDED_LONG_EDGE imposes each consecutive pair of pages
833      * upon front and back sides of consecutive media sheets, such that the
834      * orientation of each pair of pages on the medium would be correct for
835      * the reader as if for binding on the long edge. This imposition is
836      * sometimes called <i>duplex</i>. SidesType.TWO_SIDED_SHORT_EDGE imposes
837      * each consecutive pair of pages upon front and back sides of consecutive
838      * media sheets, such that the orientation of each pair of pages on the
839      * medium would be correct for the reader as if for binding on the short
840      * edge. This imposition is sometimes called <i>tumble</i>. This attribute
841      * is updated to the value chosen by the user.
842      *
843      * @return  SidesType.ONE_SIDED, SidesType.TWO_SIDED_LONG_EDGE, or
844      *          SidesType.TWO_SIDED_SHORT_EDGE.
845      */
846     public SidesType getSides() {
847         return sides;
848     }
849 
850     /**
851      * Specifies how consecutive pages should be imposed upon the sides of the
852      * print medium for jobs using these attributes. SidesType.ONE_SIDED
853      * imposes each consecutive page upon the same side of consecutive media
854      * sheets. This imposition is sometimes called <i>simplex</i>.
855      * SidesType.TWO_SIDED_LONG_EDGE imposes each consecutive pair of pages
856      * upon front and back sides of consecutive media sheets, such that the
857      * orientation of each pair of pages on the medium would be correct for
858      * the reader as if for binding on the long edge. This imposition is
859      * sometimes called <i>duplex</i>. SidesType.TWO_SIDED_SHORT_EDGE imposes
860      * each consecutive pair of pages upon front and back sides of consecutive
861      * media sheets, such that the orientation of each pair of pages on the
862      * medium would be correct for the reader as if for binding on the short
863      * edge. This imposition is sometimes called <i>tumble</i>. Not specifying
864      * this attribute is equivalent to specifying SidesType.ONE_SIDED.
865      *
866      * @param   sides SidesType.ONE_SIDED, SidesType.TWO_SIDED_LONG_EDGE, or
867      *          SidesType.TWO_SIDED_SHORT_EDGE.
868      * @throws  IllegalArgumentException if sides is null.
869      */
870     public void setSides(SidesType sides) {
871         if (sides == null) {
872             throw new IllegalArgumentException("Invalid value for attribute "+
873                                                "sides");
874         }
875         this.sides = sides;
876     }
877 
878     /**
879      * Sets how consecutive pages should be imposed upon the sides of the
880      * print medium for jobs using these attributes to the default. The
881      * default imposition is SidesType.ONE_SIDED.
882      */
883     public void setSidesToDefault() {
884         setSides(SidesType.ONE_SIDED);
885     }
886 
887     /**
888      * Returns, for jobs using these attributes, the last page (inclusive)
889      * to be printed, if a range of pages is to be printed. This attribute is
890      * updated to the value chosen by the user. An application should ignore
891      * this attribute on output, unless the return value of the <code>
892      * getDefaultSelection</code> method is DefaultSelectionType.RANGE. An
893      * application should honor the return value of <code>getPageRanges</code>
894      * over the return value of this method, if possible.
895      *
896      * @return  an integer greater than zero and greater than or equal
897      *          to <i>toPage</i> and greater than or equal to <i>minPage</i>
898      *          and less than or equal to <i>maxPage</i>.
899      */
900     public int getToPage() {
901         if (toPage != 0) {
902             return toPage;
903         } else if (fromPage != 0) {
904             return fromPage;
905         } else if (pageRanges != null) {
906             return prLast;
907         } else {
908             return getMinPage();
909         }
910     }
911 
912     /**
913      * Specifies, for jobs using these attributes, the last page (inclusive)
914      * to be printed, if a range of pages is to be printed.
915      * If this attribute is not specified, then the values from the pageRanges
916      * attribute are used. If pageRanges and either or both of fromPage and
917      * toPage are specified, pageRanges takes precedence. Specifying none of
918      * pageRanges, fromPage, or toPage is equivalent to calling
919      * setPageRanges(new int[][] { new int[] { <i>minPage</i> } });
920      *
921      * @param   toPage an integer greater than zero and greater than or equal
922      *          to <i>fromPage</i> and greater than or equal to <i>minPage</i>
923      *          and less than or equal to <i>maxPage</i>.
924      * @throws  IllegalArgumentException if one or more of the above
925      *          conditions is violated.
926      */
927     public void setToPage(int toPage) {
928         if (toPage <= 0 ||
929             (fromPage != 0 && toPage < fromPage) ||
930             toPage < minPage ||
931             toPage > maxPage) {
932             throw new IllegalArgumentException("Invalid value for attribute "+
933                                                "toPage");
934         }
935         this.toPage = toPage;
936     }
937 
938     /**
939      * Determines whether two JobAttributes are equal to each other.
940      * <p>
941      * Two JobAttributes are equal if and only if each of their attributes are
942      * equal. Attributes of enumeration type are equal if and only if the
943      * fields refer to the same unique enumeration object. A set of page
944      * ranges is equal if and only if the sets are of equal length, each range
945      * enumerates the same pages, and the ranges are in the same order.
946      *
947      * @param   obj the object whose equality will be checked.
948      * @return  whether obj is equal to this JobAttribute according to the
949      *          above criteria.
950      */
951     public boolean equals(Object obj) {
952         if (!(obj instanceof JobAttributes)) {
953             return false;
954         }
955         JobAttributes rhs = (JobAttributes)obj;
956 
957         if (fileName == null) {
958             if (rhs.fileName != null) {
959                 return false;
960             }
961         } else {
962             if (!fileName.equals(rhs.fileName)) {
963                 return false;
964             }
965         }
966 
967         if (pageRanges == null) {
968             if (rhs.pageRanges != null) {
969                 return false;
970             }
971         } else {
972             if (rhs.pageRanges == null ||
973                     pageRanges.length != rhs.pageRanges.length) {
974                 return false;
975             }
976             for (int i = 0; i < pageRanges.length; i++) {
977                 if (pageRanges[i][0] != rhs.pageRanges[i][0] ||
978                     pageRanges[i][1] != rhs.pageRanges[i][1]) {
979                     return false;
980                 }
981             }
982         }
983 
984         if (printer == null) {
985             if (rhs.printer != null) {
986                 return false;
987             }
988         } else {
989             if (!printer.equals(rhs.printer)) {
990                 return false;
991             }
992         }
993 
994         return (copies == rhs.copies &&
995                 defaultSelection == rhs.defaultSelection &&
996                 destination == rhs.destination &&
997                 dialog == rhs.dialog &&
998                 fromPage == rhs.fromPage &&
999                 maxPage == rhs.maxPage &&
1000                 minPage == rhs.minPage &&
1001                 multipleDocumentHandling == rhs.multipleDocumentHandling &&
1002                 prFirst == rhs.prFirst &&
1003                 prLast == rhs.prLast &&
1004                 sides == rhs.sides &&
1005                 toPage == rhs.toPage);
1006     }
1007 
1008     /**
1009      * Returns a hash code value for this JobAttributes.
1010      *
1011      * @return  the hash code.
1012      */
1013     public int hashCode() {
1014         int rest = ((copies + fromPage + maxPage + minPage + prFirst + prLast +
1015                      toPage) * 31) << 21;
1016         if (pageRanges != null) {
1017             int sum = 0;
1018             for (int i = 0; i < pageRanges.length; i++) {
1019                 sum += pageRanges[i][0] + pageRanges[i][1];
1020             }
1021             rest ^= (sum * 31) << 11;
1022         }
1023         if (fileName != null) {
1024             rest ^= fileName.hashCode();
1025         }
1026         if (printer != null) {
1027             rest ^= printer.hashCode();
1028         }
1029         return (defaultSelection.hashCode() << 6 ^
1030                 destination.hashCode() << 5 ^
1031                 dialog.hashCode() << 3 ^
1032                 multipleDocumentHandling.hashCode() << 2 ^
1033                 sides.hashCode() ^
1034                 rest);
1035     }
1036 
1037     /**
1038      * Returns a string representation of this JobAttributes.
1039      *
1040      * @return  the string representation.
1041      */
1042     public String toString() {
1043         int[][] pageRanges = getPageRanges();
1044         String prStr = "[";
1045         boolean first = true;
1046         for (int i = 0; i < pageRanges.length; i++) {
1047             if (first) {
1048                 first = false;
1049             } else {
1050                 prStr += ",";
1051             }
1052             prStr += pageRanges[i][0] + ":" + pageRanges[i][1];
1053         }
1054         prStr += "]";
1055 
1056         return "copies=" + getCopies() + ",defaultSelection=" +
1057             getDefaultSelection() + ",destination=" + getDestination() +
1058             ",dialog=" + getDialog() + ",fileName=" + getFileName() +
1059             ",fromPage=" + getFromPage() + ",maxPage=" + getMaxPage() +
1060             ",minPage=" + getMinPage() + ",multiple-document-handling=" +
1061             getMultipleDocumentHandling() + ",page-ranges=" + prStr +
1062             ",printer=" + getPrinter() + ",sides=" + getSides() + ",toPage=" +
1063             getToPage();
1064     }
1065 }